// inline function defination
#define int128_t __int128_t
#define uint128_t __uint128_t

static inline uint64_t as_uint64(double f)
{
  union
  {
    double f;
    uint64_t i;
  } u = {f};
  return u.i;
}

// 取双精度浮点数的高12位
static inline uint32_t top12(double x)
{
  return as_uint64(x) >> 52;
}

// return (2^52+mantissa)*(2^e)
static inline double get_double(uint64_t mantissa, int e)
{
  union
  {
    uint64_t i;
    double f;
  } u = { mantissa };
  uint64_t top12= ((uint64_t)(e+DBL_EXP_BASE) << 52);
  u.i |= top12;
  return u.f;
}

// 计算２^e, -1022 <= e <= 1023
static inline double pow2(int e)
{
  union
  {
    uint64_t i;
    double f;
  } u = {((uint64_t)(e+DBL_EXP_BASE) << 52) };
  return u.f;
}

// return x / 2^64, here x < 2^52
static inline double div_2p64(uint64_t x)
{
   union
   {
      uint64_t i;
      double d;
   } u;
   u.i = ((1023lu-12)<<52) | x;
   return (u.d - 0x1.0p-12);
}

/* 移去 "inline"后,编译器生成的函数double_2_fixpoint的汇编代码
double_2_fixpoint:
	addsd	xmm0, QWORD PTR .LC3[rip]
	movq	rax, xmm0
	sal	rax, 13
	sar	rax, 9
	ret
*/

// 将x转化为定点数表示，要求x<1.0/128,
static inline int64_t double_2_fixpoint(double x)
{
   union
   {
     double f;
     int64_t i;
  } u = { 0x1.8p-5+x}; 
  // 0x1.8p-5的IEEE754表示中，尾数部分最高位b51为１，其余51位都是0,
  // 此时，b51的权重2^-6，b50的权重为2^-7,b0的权重为2^-57
  // 当x<1.0/128时，x和0x1.8p-5相加时，x需做对齐操作，令和0x1.8p-5对齐后为x1,则此时
  // x1的b50的权重为2^-6,b51的权重为2^-7,因为x<2^-7,容易知道，x1的b50,和b51都为0
  // 当x1是正数时，u的值的IEEE754表示中，b51的值是1,b50的值是0,这两位保持不变
  // 当x1是负数时，u的值的IEEE754表示中，由于b50不够减，向b51借位,这时,b51的值变为0,b50是值变为1
  // 将u看作64位整数，左移13位后,当x是负数的,最高位为1,当x正数的,最高位为0,恰好是整数的补码数表示法一致
  // 将u左移13位后,b0移到b13,其权重实为2^-57,相应地，最低位b0的权重变为2^-70,
  // 我们的定点数的表示中,最低位权重为2^-61,故需再右移9位.
  u.i = (u.i << 13) >>9;
  return u.i;
}

/* 移去 "static inline"后,编译器生成的函数fixpoint_mul的汇编代码
	mov　rax, rdi
	imul rsi
	shrd rax, rdx, 61
	ret
*/
// 这里我们定义定点数的表示法
// 定点数采用64位整数来表示,采用补码格式,包括1位符号位,2位整数位,61位小数位
// 最高位是符号位,次高位的权重是2,次次高位的权重是１,最低位的权重是2^-61
// 当最高位为0,可表示[0-4)区间的浮点数,当最高位为1,表示(-4,0)区间的浮点数
static inline int64_t fixpoint_mul(int64_t a, int64_t b)
{
   int128_t r=(int128_t)a * (int128_t)b;
   return (int64_t)(r >> 61);
}

//将定点数转化为浮点数
static inline double fixpoint_2_double(int64_t i)
{
   return (double)i * 0x1.0p-61;
}

#ifdef _DEBUG_
#include <stdio.h>
#include <stdlib.h>
static inline void dump_double(const char* prompt,double x)
{
  double _x=x;
  unsigned char *p =(unsigned char*)(&x);
  printf("%s",prompt);
  for (int i=15;i>=0;i--)
  {
    unsigned char c=p[i/2];
    if ( i%2==1)
      printf("%x",c>>4);
    else
      printf("%x",(c&15));  
    if (i==13)  
    printf(" ");
  } 
  printf("\n");
}
#endif

